#include "defines.h"

void delayX10us(uchar n)
{
#if FoscMhz ==12
#  ifdef Fosc_1T
#     define C_INLOOP()  i=5; while(--i) {_nop_(); _nop_(); _nop_(); _nop_(); } // 10us -DEC(3/12)-MOV(1/12)-SJMP(3/12) -MOV(3/12)-JZ(3/12)-[DEC(3/12)+MOV(1/12)+SJMP(3/12) +5/12]*9
#     define C_OUTLOOP() i=5; while(--i) {_nop_(); _nop_(); _nop_();} // 10us - MOV(3/12) -LCALL(6/12) -RET(4/12) -MOV(3/12) -[7/12 +3/12]*10
#  else
#     define C_INLOOP()  _nop_(); _nop_(); _nop_(); _nop_() // 10us -DEC(1) -MOV(1) - SJMP(2)
#     define C_OUTLOOP() _nop_(); _nop_(); _nop_();         // 10 us- MOV(1) - LCALL(2) - IRET(2)
#  endif // Fosc_1T
#endif // 12M

#if FoscMhz ==24
	data uchar i;

#   ifdef Fosc_1T
#     define C_INLOOP()  i=10; while(--i) {_nop_(); _nop_(); _nop_(); _nop_(); }
#     define C_OUTLOOP() i=11; while(--i) {_nop_(); _nop_(); _nop_();}
#   else
#     define C_INLOOP()  i=4; while(--i); _nop_(); _nop_(); _nop_();
#     define C_OUTLOOP() i=4; while(--i)
#  endif // Fosc_1T
#endif

	while(--n) // MOV DPTR,#i?041;(24c,3c) MOV A,#0FFH;(12c,2c) MOV B,A;(12,1) LCALL ?C?IILDX;(24,6) ORL A,B;(12,2)	JZ ?C0003;(24,3)
	{ C_INLOOP(); }

	C_OUTLOOP();
	/*
	#if Freq_Fosc == 12000 
	// at 12Mhz, a machine cycle equals to 1us, both LCALL and RET takes 2 cycle, so make _nop_() 6 times for 10us

	while(--n) // DEC R7(12T=12c,1T=3c); MOV A,R7(12T=12c,1T=1c); JZ ?C0002(12T=24c,1T=3c);
	{
	// each loop takes 3 cycles, this loop makes (3*1 + 7*1) * (n-1) us 
	_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); //7 times, (12T=12c,1T=1c)
	} // SJMP ?C0001 (12T=24c,1T=6c)

	_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); // add additional 5 times of _nop_(), plus 4 (from LCALL and IRET) + 1 (from the parameter n),
	//so meet the 10*(n-1) +5 +4 +1 = 10*n us
	// #elif 
	#endif // OFS_FREQ

	// For the call nesting, be aware LCALL are counted for every nesting layer but ONLY one most inner IRET is counted
	// for example, delay12us() {delay10us();} delay32us(){delay10us(); delay10us(); delay10us();} delay66us(){delay32us(); delay32us();} and so on
	*/
}

void delay1ms()
{
#if FoscMhz ==12
#  ifdef Fosc_1T
	delayX10us(1);
#  else
	_nop_(); _nop_(); _nop_(); _nop_();
	_nop_(); _nop_(); _nop_(); _nop_(); // 8 times in addition to the LCALL and parameter n to 10us
#  endif // Fosc_1T
#endif // 12M

#if FoscMhz ==24
	delayX10us(1);
#endif

	delayX10us(99);
}

void delayXms(uint n) // (n,error%) are (1,-1%), (2,-0.35%) (3,0.03%) (4,0.05%) (5,0.04%)...(10,0.17%)...(20,0.24%)...(100, 0.287%)...(INFINITE, 0.3%)
{
	delayX10us(99); // almost 1ms.
	while(--n) // each loop takes 3 cycles, this loop makes (3*1 + 1000) * (n-1) us
		delay1ms();
}

void delayIOSet()
{
	_nop_();

#ifdef Fosc_1T
	// it normally took 4 cycle to get stable when change the io pin
	_nop_();
#endif // INST_T
}

#ifdef STC12C5AXX // sfr IAP_CONTR   = 0xC7;
void MCU_softReset()
{
	IAP_CONTR =0x20; 
}

void MCU_softResetToISP()
{
	IAP_CONTR =0x60; 
}
#endif // STC12C5AXX

// -----------------------------
// UART funcs
// -----------------------------
typedef struct _UART_baud
{
	uint   hbps; // baudrate in hundred bps
	uint   reload; // the reload value
} UART_baud;

code UART_baud UART_baudTable[] = {

#ifdef Fosc_1T // ======== 1T chip ===============
// http://www.keil.com/products/c51/baudrate.asp, clockdiv=1, timer="time1,smod=0"

#if FoscMhz == 11 // 11.0592Mhz
	// 0FDh	115,200.00	115,200	0.00%
	// 0FAh	57,600.00	57,600	0.00%
	// 0F7h	38,400.00	38,400	0.00%
	// 0F4h	28,800.00	28,800	0.00%
	// 0EEh	19,200.00	19,200	0.00%
	// 0E8h	14,400.00	14,400	0.00%
	// 0DCh	9,600.00	9,600	0.00%
	// 0B8h	4,800.00	4,800	0.00%
	//  70h	2,400.00	2,400	0.00%
	{  24, 0x70}, // error=0%
	{  48, 0xb8}, // error=0%
	{  96, 0xdc}, // error=0%
	{ 144, 0xe8}, // error=0%
	{ 192, 0xee}, // error=0%
	{ 288, 0xf4}, // error=0%
	{ 384, 0xf7}, // error=0%
	{ 576, 0xfa}, // error=0%
	{1152, 0xfd}, // error=0%
#endif // 11.0592Mhz

#  if FoscMhz == 12 // 12Mhz
	// 0F6h	37,500.00	38,400	-2.34% *
	// 0F3h	28,846.15	28,800	0.16%
	// 0ECh	18,750.00	19,200	-2.34% *
	// 0E6h	14,423.08	14,400	0.16%
	// 0D9h	9,615.38	9,600	0.16%
	// 0B2h	4,807.69	4,800	0.16%
	//  64h	2,403.85	2,400	0.16%

	{  24, 0x64}, // err=0.16%
	{  48, 0xb2}, // err=0.16%
	{  96, 0xd9}, // err=0.16%
	{ 144, 0xe6}, // err=0.16%
	{ 192, 0xe4}, // err=-2.34%
	{ 288, 0xf3}, // err=0.16%
	{ 384, 0xf6}, // err=-2.34%
#  endif // 12Mhz

#if FoscMhz == 22 // 22.1184Mhz
	// 0FAh	115,200.00	115,200	0.00%
	// 0F4h	57,600.00	57,600	0.00%
	// 0EEh	38,400.00	38,400	0.00%
	// 0E8h	28,800.00	28,800	0.00%
	// 0DCh	19,200.00	19,200	0.00%
	// 0D0h	14,400.00	14,400	0.00%
	// 0B8h	9,600.00	9,600	0.00%
	//  70h	4,800.00	4,800	0.00%
	{  48, 0x70}, // error=0%
	{  96, 0xb8}, // error=0%
	{ 144, 0xd0}, // error=0%
	{ 192, 0xdc}, // error=0%
	{ 288, 0xe8}, // error=0%
	{ 384, 0xee}, // error=0%
	{ 576, 0xf4}, // error=0%
	{1152, 0xfa}, // error=0%
#endif // 22.1184Mhz

#if FoscMhz == 24 // 24Mhz
	// 0F3h	57,692.31	57,600	0.16%
	// 0ECh	37,500.00	38,400	-2.34% *
	// 0E6h	28,846.15	28,800	0.16%
	// 0D9h	19,230.77	19,200	0.16%
	// 0CCh	14,423.08	14,400	0.16%
	// 0B2h	9,615.38	9,600	0.16%
	//  64h	4,807.69	4,800	0.16%
	{  48,   0x64}, // error=0.16%
	{  96,   0xb2}, // error=0.16%
	{ 144,   0xcc}, // error=0.16%
	{ 192,   0xd9}, // error=0.16%
	{ 288,   0xe6}, // error=0.16%
	{ 384,   0xec}, // error=-2.34%
	{ 576,   0xf3}, // error=0.16%
#endif // 24Mhz

#else // ======== !Fosc_1T, 12T chip, take T2 as the generator, T2CON = 0x34 ===============
	// http://www.keil.com/products/c51/baudrate.asp, clockdiv=32, timer="t2con=0x34"
#undef SMOD_1 // no SMOD_1 allowed

#if FoscMhz == 11 // 11.0592Mhz
	// 0FFh , 0FDh	115,200.00	115,200	0.00%
	// 0FFh , 0FAh	57,600.00	57,600	0.00%
	// 0FFh , 0F7h	38,400.00	38,400	0.00%
	// 0FFh , 0F4h	28,800.00	28,800	0.00%
	// 0FFh , 0EEh	19,200.00	19,200	0.00%
	// 0FFh , 0E8h	14,400.00	14,400	0.00%
	// 0FFh , 0DCh	9,600.00	9,600	0.00%
	// 0FFh , 0B8h	4,800.00	4,800	0.00%
	// 0FFh , 70h	2,400.00	2,400	0.00%
	// 0FEh , 0E0h	1,200.00	1,200	0.00%
	{ 12,  0xfee0}, // 1200, 0.00%
	{ 24,  0xff70}, // 2400, 0.00%
	{ 48,  0xffb8}, // 4800, 0.00%
	{ 96,  0xffdc}, // 9600, 0.00%
	{144,  0xffe8}, // 9600, 0.00%
	{192,  0xffee}, //19600, 0.00%
	{288,  0xfff4}, //28800, 0.00%
	{384,  0xfff7}, //28800, 0.00%
	{576,  0xfffa}, //28800, 0.00%
	{1152, 0xfffd}, //28800, 0.00%
#endif // 11.0592Mhz

#if FoscMhz == 12 // 12Mhz
	// 0FFh , 0F3h	28,846.15	28,800	0.16%
	// 0FFh , 0E6h	14,423.08	14,400	0.16%
	// 0FFh , 0D9h	9,615.38	9,600	0.16%
	// 0FFh , 0B2h	4,807.69	4,800	0.16%
	// 0FFh , 64h	2,403.85	2,400	0.16%
	// 0FEh , 0C8h	1,201.92	1,200	0.16%
	{  12,   0xfec8}, // error=0.16%
	{  24,   0xff64}, // error=0.16%
	{  48,   0xffb2}, // error=0.16%
	{  96,   0xffd8}, // error=0.16% // {  96,   0xffd9}, // error=0.16%
	{ 144,   0xffe6}, // error=0.16%
	{ 288,   0xfff3}, // error=0.16%
#endif // 12Mhz

#if FoscMhz == 22 // 22.1184Mhz
	// 0FFh , 0FAh	115,200.00	115,200	0.00%
	// 0FFh , 0F4h	57,600.00	57,600	0.00%
	// 0FFh , 0EEh	38,400.00	38,400	0.00%
	// 0FFh , 0E8h	28,800.00	28,800	0.00%
	// 0FFh , 0DCh	19,200.00	19,200	0.00%
	// 0FFh , 0D0h	14,400.00	14,400	0.00%
	// 0FFh , 0B8h	9,600.00	9,600	0.00%
	// 0FFh , 70h	4,800.00	4,800	0.00%
	// 0FEh , 0E0h	2,400.00	2,400	0.00%
	// 0FDh , 0C0h	1,200.00	1,200	0.00%
	{  12, 0xffc0}, // error=0%
	{  24, 0xffe0}, // error=0%
	{  48, 0xff70}, // error=0%
	{  96, 0xffb8}, // error=0%
	{ 144, 0xffd0}, // error=0%
	{ 192, 0xffdc}, // error=0%
	{ 288, 0xffe8}, // error=0%
	{ 384, 0xffee}, // error=0%
	{ 576, 0xfff4}, // error=0%
	{1152, 0xfffa}, // error=0%
#endif // 22.1184Mhz

#if FoscMhz == 24 // 24Mhz
	//0FFh , 0F3h	57,692.31	57,600	0.16%
	//0FFh , 0E6h	28,846.15	28,800	0.16%
	//0FFh , 0D9h	19,230.77	19,200	0.16%
	//0FFh , 0CCh	14,423.08	14,400	0.16%
	//0FFh , 0B2h	9,615.38	9,600	0.16%
	//0FFh , 64h	4,807.69	4,800	0.16%
	//0FEh , 0C8h	2,403.85	2,400	0.16%
	//0FDh , 8Fh	1,200.00	1,200	0.00%
	{  12, 0xfd8f}, // error=0.00%
	{  24, 0xfec8}, // error=0.16%
	{  48, 0xff64}, // error=0.16%
	{  96, 0xffb2}, // error=0.16%
	{ 144, 0xffcc}, // error=0.16%
	{ 192, 0xffd9}, // error=0.16%
	{ 288, 0xffe6}, // error=0.16%
	{ 576, 0xfff3}, // error=0.16%
#endif // 24Mhz

#endif // !Fosc_1T
	{   0, 0x00}
};

uint UART_init(uint baudHbps)
{
	uchar i;

	EA      = 0;    // disallow all interrupts
	SCON    = 0x50; // 0101,0000 mode 1 8n1, 10bit-per-byte: startbit=0, 8bit-data, stopbit=1

#ifdef SMOD_1 // SMOD=1 double speed
	PCON   |= 0x80;
	baud   >>=1; // take 1/2 of the wished baud to scan the BaudTable
#endif // SMOD_1

	// scan the baud table for the wished baud rate
	for (i=0; UART_baudTable[i].hbps; i++)
	{
		if (!UART_baudTable[i+1].hbps)
			break;

		if (UART_baudTable[i+1].hbps > baudHbps)
			break;
	}

	// determ the reload value
#ifdef Fosc_1T
	// take the BRT to determine baud: BRTR=1, S1BRS=1
	AUXR   |= 0x15;      // T0x12,T1x12,UART_M0x6,BRTR=1, S2SMOD,BRTx12,EXTRAM,S1BRS=1	
	BRT     = UART_baudTable[i].reload & 0xff;
#else
	// set the baud rate
	T2CON = 0x34;  // take T2 as the baud generator
	TMOD |= 0x20;  //??? not sure

	// always take T2 as the timer
	RCAP2L = UART_baudTable[i].reload & 0xff;
	RCAP2H = UART_baudTable[i].reload >> 8;
#endif // Fosc_1T

	baudHbps = UART_baudTable[i].hbps;
#ifdef SMOD_1 // SMOD=1 double speed
	baudHbps   <<=1; // double the baud back
#endif // SMOD_1

	TI      = 1;    // clear the SBUF
	ES      = 1;    // allow UART interrupt
	EA      = 1;    // allow interrupts

	UART_485Dir(0);

	return baudHbps;
}

uchar UART_read(uchar* buf, uchar maxLen)
{
	uchar i, j;

#ifndef UART_DIR_BATCH
	UART_485Dir(0);
#endif // UART_DIR_BATCH

	if (!buf)
		return 0;

	for (i=0; i < maxLen; i++)
	{
		for(j = 255; !RI && j; j--)
			delayX10us(2);

		if (!j)
			return i;
		
		RI=0;
		buf[i] = SBUF;
	}

	return i;	
}

uchar UART_readbyte(void)
{
	uchar v =0;
	return (UART_read(&v, 1) ? v : 0);
}

/*
uchar UART_readbyte(void)
{
	uchar value=200;

	for(;!RI && value; value--)
		delayX10us(2);
	if (!value)
		return 0x00;

	RI=0;
	value = SBUF;
	return value;	
}
*/

uchar UART_write(uchar* buf, uchar maxLen)
{
	uchar i, j;

	if (!buf)
		return 0;

#ifndef UART_DIR_BATCH
	UART_485Dir(1);
#endif // UART_DIR_BATCH

	for (i=0; i < maxLen; i++)
	{
		TI =0;
		SBUF = buf[i];
		while(!TI); // wait for the sending gets completed

		for(j=255; !TI && j; j--)
			delayX10us(2);
		TI =0; // reset TI
	}

#ifndef UART_DIR_BATCH
	UART_485Dir(0);
#endif // UART_DIR_BATCH
	return i;
}

void UART_writebyte(uchar value)
{
	UART_write(&value, 1);
}

/*
void UART_writebyte(uchar value)
{
#ifndef UART_DIR_BATCH
	UART_485Dir(1);
#endif // UART_DIR_BATCH

	TI =0;
	SBUF = value;
	while(!TI); // wait for the sending gets completed

	for(value=200; !TI && value; value--)
		delayX10us(2);
	TI =0; // reset TI

#ifndef UART_DIR_BATCH
	UART_485Dir(0);
#endif // UART_DIR_BATCH
}
*/

uint UART_readword(void)
{
#ifdef DPTR
	DPL = UART_readbyte();  DPH = UART_readbyte(); // LSBF
	return DPTR;
#else
	uint value = UART_readbyte();
	value |= ((uint) UART_readbyte()) <<8;
	return value;
#endif // DPTR
}

void UART_writeword(uint value)
{
#ifdef DPTR
	DPTR = value; 
	UART_writebyte(DPL); UART_writebyte(DPH); // LSBF
#else
	UART_writebyte((uchar)(value & 0xff));
	UART_writebyte((uchar)((value >>8) & 0xff));
#endif // DPTR
}


uint T0_reload =0;
void Timer0_reload(void)
{
	TH0= T0_reload >> 8;//???,1ms??
	TL0= T0_reload & 0xff; // the low byte
}

void Timer0_init(uint Xmsec)
{
#ifdef Fosc_1T
	T0_reload = 65536 - Xmsec *FoscMhz*1000;
#else // assume the MCU takes 12T per instruction
	T0_reload = 65536 - Xmsec *FoscMhz*1000/12;
#endif // Fosc_T

	TMOD=0x01; // set T0 to run at mode 1
	Timer0_reload();
	ET0=1; // enable timer 0 interrupt
	TR0=1; // start timer0
	EA=1;  // enable interrupts
}

bool FIFO_init(FIFO_t* fifo, uint8 buf[], uint8 size)
{
	if (NULL == fifo || NULL == buf || size <= 0)
		return false;

	fifo->header = 0;
	fifo->tail   = 0;
	fifo->size   = size;
	fifo->buf    = buf;
	return true;
}

bool FIFO_tryWrite(FIFO_t* fifo, uint8 dat)
{
	uint8 newheader = fifo->header+1; 
	if (NULL == fifo || NULL == fifo->buf)
		return false;

	if (newheader >= fifo->size)
		newheader =0;

	if (newheader == fifo->tail)
		return false;

	fifo->buf[fifo->header] = dat;
	fifo->header = newheader;
	return true;
}

bool FIFO_tryRead(FIFO_t* fifo, uint8* dat)
{
	uint8 newtail = fifo->tail+1; 
	if (newtail >= fifo->size)
		newtail =0;

	if (newtail == fifo->header || fifo->tail == fifo->header)
		return false;

	*dat = fifo->buf[fifo->tail];
	fifo->tail = newtail;
	return true;
}

bool FIFO_write(FIFO_t* fifo, uint8 dat)
{
	if (NULL == fifo)
		return false;

	while(!FIFO_tryWrite(fifo, dat))
		delayX10us(1);

	return true;
}

// volatile MsgLine _pendingLines[MAX_PENDMSGS];
volatile MsgLine* MsgLine_findLineToFill(uint8 chId) reentrant
{
	int i =0;
	volatile MsgLine* thisMsg = NULL;

	for (i =0; i < MAX_PENDMSGS; i++)
	{
		if (0 != _pendingLines[i].timeout)
		{
			 _pendingLines[i].timeout--;
			 if (_pendingLines[i].chId == chId && _pendingLines[i].offset >0)
			 	thisMsg = &_pendingLines[i]; // continue with the recent incomplete receving;
		}
		else if (NULL == thisMsg) // take the first idle pend msg
			thisMsg = &_pendingLines[i];
	}

	return thisMsg;
}

void MsgLine_recv(uint8 chId, uint8* inBuf, uint8 maxLen)
{
	volatile MsgLine* thisMsg = MsgLine_findLineToFill(chId);

	if (NULL == thisMsg) // failed to find an available idle pend buffer, simply give up this receiving
		return;

 #define buf_i (thisMsg->offset)
 #define buf   (thisMsg->msg)

 	thisMsg->chId  = chId;
	thisMsg->timeout = (MAX_PENDMSGS<<2)+3; // refill the timeout as 4 times of the buffer

	for (; maxLen>0 && *inBuf; maxLen--)
	{
		buf[buf_i] = *inBuf++;

		// a \0 is known as the data end of this received frame
		if ('\0' == buf[buf_i])
			break;

		// an EOL would be known as a message end
		if ('\r' != buf[buf_i] && '\n' != buf[buf_i])
		{
			buf_i = (++buf_i) % (MAX_MSGLEN-3);
			continue;
		}

		// a line just received
		if (buf_i < MAX_MSGLEN -3) // re-fill in the line-end if the buffer is enough
		{ buf[buf_i++] = '\r'; buf[buf_i++] = '\n'; }
		
		buf[buf_i] = '\0'; // NULL terminate the string
		buf_i =0; // reset for next receiving

		if ('\0' == buf[0]) // empty line, ignore
			continue;

		// a valid line of message here
		// OnWirelessTextMsg(chip->nodeId, pipeId-1, (char*)buf);
		break;
	}
}

uint8 hexchval(char ch)
{
	if (ch >= '0' && ch <= '9')
		return ch - '0';

	if (ch >= 'A' && ch <= 'F')
		return ch - 'A' +10;

	if (ch >= 'a' && ch <= 'f')
		return ch - 'a' +10;

	return 0xff;
}

const char* hex2int(const char* hexstr, uint16* pVal)
{
	uint8 v =0;
	if (NULL == hexstr || NULL == pVal)
		return NULL;

	for(*pVal =0; *hexstr; hexstr++)
	{
		v = hexchval(*hexstr);
		if (v >0x0f) break;
		*pVal <<=4; *pVal += v;
	}

	return hexstr;
}


